import random  # 模拟使用，实际应用中替换为真实传感器数据
from PyQt5 import QtWidgets, QtCore
from main import PIDController
import pyqtgraph as pg

class PIDControlSystem:
    def __init__(self):
        from VirtualTimer import PauseableTimer
        # 设置UI
        from PyQt5.uic import loadUi
        self.ui: QtWidgets.QWidget = loadUi('main.ui')
        self.ui.closeEvent = self.closeEvent
        # 初始化PID控制器
        self.pid = PIDController(kp=1.0, ki=0., kd=0.0)
        
        # 系统状态
        self.running = False  # 控制是否正在运行
        self.paused = False  # 控制是否被暂停
        self.setpoint = 0.0
        self.measured_value = 0.0
        self.control_output = 0.0
        
        # 时间相关 - 现在完全由QTimer管理
        self.start_time = 0.0  # 将在开始控制时设置为当前时间
        self.elapsed_time = 0.0  # 从开始时间以来的时间(秒)
        
        # 数据存储
        self.measured_data = []  # 存储测量值
        self.setpoint_data = []  # 存储设定值
        self.output_data = []  # 存储控制输出
        self.time_data = []  # 存储时间数据(从start_time开始的秒数)
        
        # 最大数据点数(避免内存问题)
        self.max_data_points = 200
        
        # 设置定时器用于控制循环
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.control_loop)
        self.timer.setInterval(100)  # 100ms更新一次
        
        # 设置一个高精度计时器用于跟踪经过的时间
        # 使用QElapsedTimer来获得更精确的时间测量
        self.elapsed_timer = PauseableTimer()

        # 初始化显示
        self.update_display()
        
        # 设置PyQtGraph图表
        self.setup_plot()
        
        # 连接信号和槽
        self.ui.startButton.clicked.connect(self.toggle_control)
        self.ui.pauseButton.clicked.connect(self.toggle_pause)  # 新增暂停按钮连接
        self.ui.setpointLineEdit.returnPressed.connect(self.update_setpoint)
        self.ui.initLineEdit.returnPressed.connect(self.update_setpoint)
        self.ui.kpLineEdit.returnPressed.connect(self.update_pid_params)
        self.ui.kiLineEdit.returnPressed.connect(self.update_pid_params)
        self.ui.kdLineEdit.returnPressed.connect(self.update_pid_params)
    
    def setup_plot(self):
        """
        设置PyQtGraph图表
        """
        # 创建图形布局
        self.graphWidget = pg.PlotWidget()
        self.ui.verticalLayout.addWidget(self.graphWidget)  # 假设verticalLayout是UI中的一个布局
        
        # 设置图形标题和轴标签
        self.graphWidget.setTitle("PID控制系统实时数据", color="r", size="15pt")
        styles = {"color": "#F00", "font-size": "15pt"}
        self.graphWidget.setLabel("left", "值", units="V", **styles)
        self.graphWidget.setLabel("bottom", "时间", units="s", **styles)
        self.graphWidget.showGrid(x=True, y=True)
        # 添加图例（通常在创建PlotWidget后）
        legend: pg.LegendItem = self.graphWidget.addLegend()
        legend.setLabelTextSize('15pt')
        # 创建三条曲线
        self.measured_curve = self.graphWidget.plot(
            pen=pg.mkPen(color=(0, 0, 255), width=2),
            name="测量值"
        )
        self.setpoint_curve = self.graphWidget.plot(
            pen=pg.mkPen(color=(0, 255, 0), width=2),
            name="设定值"
        )
        self.output_curve = self.graphWidget.plot(
            pen=pg.mkPen(color=(255, 0, 0), width=2),
            name="控制输出"
        )
        # 设置X轴范围(初始为10秒)
        self.graphWidget.setXRange(0, 10)
        # 设置Y轴范围(可根据实际情况调整)
        self.graphWidget.setYRange(-1, 5)
    
    def toggle_control(self):
        """
        开始或停止控制
        """
        if self.running:
            # 如果正在运行，则停止
            self.running = False
            self.paused = False  # 停止时也清除暂停状态
            self.ui.startButton.setText("开始")
            self.ui.pauseButton.setText("暂停")
            self.ui.pauseButton.setEnabled(False)  # 禁用暂停按钮
            self.timer.stop()  # 停止定时器
            self.elapsed_timer.reset()  # 重新计时，并清零
        else:
            # 获取设定值和PID参数
            self.update_setpoint()
            self.update_pid_params()
            
            # 清空数据(重新开始)
            self.time_data = []
            self.measured_data = []
            self.setpoint_data = []
            self.output_data = []
            
            # 记录开始时间
            self.start_time = self.elapsed_timer.elapsed() / 1000.0  # 转换为秒
            self.elapsed_time = 0.0  # 每次开始重新计算时间
            
            # 重置计时器
            self.elapsed_timer.start()
            
            self.running = True
            self.paused = False  # 确保不是暂停状态
            self.ui.startButton.setText("停止")
            self.ui.pauseButton.setEnabled(True)  # 启用暂停按钮
            self.timer.start()  # 开始控制循环
    
    def toggle_pause(self):
        """
        切换暂停状态
        """
        if not self.running:
            return  # 如果没有运行，忽略暂停操作
        
        if self.paused:
            # 如果当前是暂停状态，则恢复运行
            self.paused = False
            self.ui.pauseButton.setText("暂停")
            # 获取设定值和PID参数
            self.update_setpoint()
            self.update_pid_params()
            self.elapsed_timer.start()  # 暂停状态结束，继续计时
            self.timer.start()  # 重新启动定时器
        else:
            # 如果当前是运行状态，则暂停
            self.paused = True
            self.ui.pauseButton.setText("继续")
            self.timer.stop()  # 停止定时器
            self.ui.initLineEdit.setText(f"{self.measured_value:.4f}")  # 暂停时自动设置初始值
            self.elapsed_timer.pause()
    
    def update_setpoint(self):
        """
        从输入框更新设定值
        """
        try:
            self.setpoint = float(self.ui.setpointLineEdit.text())
            self.measured_value = float(self.ui.initLineEdit.text())
        except ValueError:
            QtWidgets.QMessageBox.warning(None, "输入错误", "设定值必须是数字")
    
    def update_pid_params(self):
        """
        从输入框更新PID参数
        """
        try:
            self.pid.kp = float(self.ui.kpLineEdit.text())
            self.pid.ki = float(self.ui.kiLineEdit.text())
            self.pid.kd = float(self.ui.kdLineEdit.text())
        except ValueError:
            QtWidgets.QMessageBox.warning(None, "输入错误", "PID参数必须是数字")
    
    def control_loop(self):
        """
        控制循环，定时执行
        """
        # 计算从开始时间以来的经过时间(秒)
        current_elapsed_ms = self.elapsed_timer.elapsed()
        self.elapsed_time = current_elapsed_ms / 1000.0  # 转换为秒
        
        # 模拟获取测量值(实际应用中替换为真实传感器读取)
        # 这里使用一个简单的模拟: 测量值会缓慢接近设定值，但有一些噪声
        noise = random.uniform(-0.5, 0.5)
        if self.running and not self.paused:  # 只有在运行且未暂停时才更新
            # 模拟系统响应
            if abs(self.measured_value - self.setpoint) < 0.1:
                # 接近设定值时，添加一些随机波动
                self.measured_value += noise * 0.01
            else:
                # 远离设定值时，向设定值移动
                direction = 1 if self.setpoint > self.measured_value else -1
                self.measured_value += direction * 0.05 + noise * 0.01
        
        # 计算PID控制输出
        # 注意: 这里我们使用固定的时间步长dt=0.1秒(100ms)，因为这是我们的定时器间隔
        dt = 0.1  # 固定时间步长，单位为秒
        if self.running and not self.paused:  # 只有在运行且未暂停时才计算控制输出
            self.control_output = self.pid.compute(self.setpoint, self.measured_value, dt)

        # 在实际应用中，这里会将控制输出发送给执行器
        
        # 更新数据存储 - 只有在运行且未暂停时才存储数据
        if self.running and not self.paused:
            self.update_data_storage()
        # 更新显示
        self.update_display()
        # 更新图表
        self.update_plot()
    
    def update_data_storage(self):
        """
        更新数据存储
        """
        # 添加新数据点
        self.time_data.append(self.elapsed_time)
        self.measured_data.append(self.measured_value)
        self.setpoint_data.append(self.setpoint)
        self.output_data.append(self.control_output)
        
        # 限制数据点数量，避免内存问题
        if len(self.time_data) > self.max_data_points:
            # 移除最早的数据点
            self.time_data.pop(0)
            self.measured_data.pop(0)
            self.setpoint_data.pop(0)
            self.output_data.pop(0)
    
    def update_plot(self):
        """
        更新图表
        """
        if len(self.time_data) > 0:
            # 更新曲线数据
            self.measured_curve.setData(self.time_data, self.measured_data)
            self.setpoint_curve.setData(self.time_data, self.setpoint_data)
            self.output_curve.setData(self.time_data, self.output_data)
            
            # 动态调整X轴范围，显示最近10秒的数据
            if len(self.time_data) > 1:
                current_max_time = self.time_data[-1]
                self.graphWidget.setXRange(max(0, current_max_time - 10), current_max_time)
    
    def update_display(self):
        """
        更新界面显示
        """
        self.ui.measuredValueLabel.setText(f"{self.measured_value:.4f}")
        self.ui.controlOutputLabel.setText(f"{self.control_output:.4f}")
    
    def closeEvent(self, event):
        """
        关闭窗口时的处理
        """
        if self.running:
            self.timer.stop()
        event.accept()

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = PIDControlSystem()
    window.ui.show()
    sys.exit(app.exec_())